package mysql

import (
	"context"
	"gorm.io/gorm"
	"mall-backend/domain/entity"
	"mall-backend/domain/repository/dao"
	"mall-backend/pkg/utils"
	"time"
)

var _ dao.IUserRepo = new(UserRepo)

func NewUserRepo(db *gorm.DB) dao.IUserRepo {
	return &UserRepo{
		db: db,
	}
}

type UserRepo struct {
	db *gorm.DB
}

func (d UserRepo) Add(ctx context.Context, user dao.User) error {
	return d.db.WithContext(ctx).Model(&dao.User{}).Debug().Create(&user).Error
}

func (d UserRepo) FindByParams(ctx context.Context, whereParams map[string]interface{}) (dao.User, error) {
	whereSql, values, _ := utils.BuildCondition(whereParams)
	var item dao.User
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).First(&item).Error
	return item, err
}

func (d UserRepo) UpdateByParams(ctx context.Context, whereParams, updateParams map[string]interface{}) error {
	whereSql, values, _ := utils.BuildCondition(whereParams)
	return d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Updates(updateParams).Error
}

func (d UserRepo) ListByUserIds(ctx context.Context, userIds []int64) ([]dao.User, int, error) {
	values := make([]interface{}, 0)
	whereSql := ""
	whereSql = utils.GenQuery(whereSql, "AND", "user_id in (?)")
	values = append(values, userIds)
	total := int64(0)
	var list []dao.User
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Order("update_time desc").Find(&list).Count(&total).Error
	return list, int(total), err
}

func (d UserRepo) ListUsers(ctx context.Context, memberType, mobileBind, wxaBind, status, isSystemUser int, from int64, to int64, page int, size int, sort string) ([]dao.User, int, error) {
	if sort == "" {
		sort = "last_active_time desc"
	}
	whereSql := ""
	values := make([]interface{}, 0)
	whereSql = utils.GenQuery(whereSql, "AND", "is_system_user = ?")
	values = append(values, isSystemUser)
	if memberType != entity.MemberTypeAll {
		if memberType == entity.MemberTypeVisit {
			whereSql = utils.GenQuery(whereSql, "AND", "( mobile_bind = ? && wxa_bind = ? )")
			values = append(values, entity.UserMobileUnBind)
			values = append(values, entity.UserWxaUnBind)
		} else {
			whereSql = utils.GenQuery(whereSql, "AND", "( mobile_bind = ? || wxa_bind = ? )")
			values = append(values, entity.UserMobileBind)
			values = append(values, entity.UserWxaBind)
		}
	}
	if mobileBind != entity.UserMobileBindAll {
		whereSql = utils.GenQuery(whereSql, "AND", "mobile_bind = ?")
		values = append(values, mobileBind)
	}
	if wxaBind != entity.UserWxaBindAll {
		whereSql = utils.GenQuery(whereSql, "AND", "wxa_bind = ?")
		values = append(values, wxaBind)
	}
	if status != entity.UserStatusAll {
		whereSql = utils.GenQuery(whereSql, "AND", "status=?")
		values = append(values, status)
	}
	if from != 0 {
		whereSql = utils.GenQuery(whereSql, "AND", "last_active_time >= ?")
		values = append(values, from)
	}
	if to != 0 {
		whereSql = utils.GenQuery(whereSql, "AND", "last_active_time <= ?")
		values = append(values, to)
	}
	total := int64(0)
	var list []dao.User
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Count(&total).Error
	if err != nil {
		return list, 0, err
	}
	if page == 0 && size == 0 {
		err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Order(sort).Find(&list).Error
	} else {
		if page == 0 {
			page = 1
		}
		if size == 0 {
			size = 10
		}
		err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Order(sort).Offset((page - 1) * size).Limit(size).Find(&list).Error
	}
	return list, int(total), err
}

func (d UserRepo) ListUserByParams(ctx context.Context, whereParams map[string]interface{}, page, size int) ([]dao.User, int, error) {
	whereSql, values, _ := utils.BuildCondition(whereParams)
	total := int64(0)
	var list []dao.User
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Count(&total).Error
	if err != nil {
		return list, 0, err
	}
	if page == 0 && size == 0 {
		err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Order("update_time desc").Find(&list).Error
	} else {
		if page == 0 {
			page = 1
		}
		if size == 0 {
			size = 10
		}
		err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Order("update_time desc").Offset((page - 1) * size).Limit(size).Find(&list).Error
	}
	return list, int(total), err
}

func (d UserRepo) ListInvite(ctx context.Context, userId int64, page, size int, startTime, endTime int64) ([]dao.User, int, error) {
	values := make([]interface{}, 0)
	whereSql := ""
	whereSql = utils.GenQuery(whereSql, "AND", "up_user_id = ?")
	values = append(values, userId)
	whereSql = utils.GenQuery(whereSql, "AND", "(mobile != ? or wxa_open_id != ?)")
	values = append(values, "")
	values = append(values, "")
	if startTime != 0 {
		whereSql = utils.GenQuery(whereSql, "AND", "real_regist_time >= ?")
		values = append(values, startTime)
	}
	if endTime != 0 {
		whereSql = utils.GenQuery(whereSql, "AND", "real_regist_time <= ?")
		values = append(values, endTime)
	}
	total := int64(0)
	var list []dao.User
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Count(&total).Error
	if err != nil {
		return list, 0, err
	}
	if page == 0 && size == 0 {
		err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Order("real_regist_time desc").Find(&list).Error
	} else {
		if page == 0 {
			page = 1
		}
		if size == 0 {
			size = 10
		}
		err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Order("real_regist_time desc").Offset((page - 1) * size).Limit(size).Find(&list).Error
	}
	return list, int(total), err
}

func (d UserRepo) CountInvite(ctx context.Context, userId int64, startTime, endTime int64) (int, error) {
	values := make([]interface{}, 0)
	whereSql := ""
	whereSql = utils.GenQuery(whereSql, "AND", "up_user_id = ?")
	values = append(values, userId)
	whereSql = utils.GenQuery(whereSql, "AND", "(mobile != ? or wxa_open_id != ?)")
	values = append(values, "")
	values = append(values, "")
	if startTime != 0 {
		whereSql = utils.GenQuery(whereSql, "AND", "real_regist_time >= ?")
		values = append(values, startTime)
	}
	if endTime != 0 {
		whereSql = utils.GenQuery(whereSql, "AND", "real_regist_time <= ?")
		values = append(values, endTime)
	}
	total := int64(0)
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Count(&total).Error
	if err != nil {
		return 0, err
	}
	return int(total), err
}

func (d UserRepo) ListFirstInvite(ctx context.Context, userId int64) ([]int64, error) {
	values := make([]interface{}, 0)
	whereSql := ""
	whereSql = utils.GenQuery(whereSql, "AND", "up_user_id = ?")
	values = append(values, userId)
	whereSql = utils.GenQuery(whereSql, "AND", "(mobile != ? or wxa_open_id != ?)")
	values = append(values, "")
	values = append(values, "")
	var userIds []int64
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Pluck("user_id", &userIds).Error
	return userIds, err
}

func (d UserRepo) CountSecondInvite(ctx context.Context, userIds []int64) (int, error) {
	values := make([]interface{}, 0)
	whereSql := ""
	whereSql = utils.GenQuery(whereSql, "AND", "up_user_id in ?")
	values = append(values, userIds)
	whereSql = utils.GenQuery(whereSql, "AND", "(mobile != ? or wxa_open_id != ?)")
	values = append(values, "")
	values = append(values, "")
	total := int64(0)
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Count(&total).Error
	return int(total), err
}

func (d UserRepo) GetLatestUser(ctx context.Context, whereParams map[string]interface{}) (dao.User, error) {
	whereSql, values, _ := utils.BuildCondition(whereParams)
	var item dao.User
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Order("create_time desc").First(&item).Error
	return item, err
}

func (d UserRepo) IncFirstInvite(ctx context.Context, whereParams map[string]interface{}, num int64) error {
	whereSql, values, _ := utils.BuildCondition(whereParams)
	return d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).UpdateColumn("first", gorm.Expr("first + ?", num)).Error
}

func (d UserRepo) IncSecondInvite(ctx context.Context, whereParams map[string]interface{}, num int64) error {
	whereSql, values, _ := utils.BuildCondition(whereParams)
	return d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).UpdateColumn("second", gorm.Expr("second + ?", num)).Error
}

func (d UserRepo) StatTotal(ctx context.Context) (*dao.StatUserTotalItem, error) {
	statTotal := &dao.StatUserTotalItem{}
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("mobile != ? or wxa_open_id != ?", "", "").Count(&statTotal.TotalVip).Error
	if err != nil {
		return nil, err
	}
	//statTotal.TotalVip = total
	err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("mobile = ? and wxa_open_id = ?", "", "").Count(&statTotal.TotalVisit).Error
	if err != nil {
		return nil, err
	}
	//statTotal.TotalVisit = total
	err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("(mobile_bind = ? && mobile != '') or (wxa_bind = ? && wxa_open_id != '') ", entity.UserMobileUnBind, entity.UserWxaUnBind).Count(&statTotal.TotalUnbind).Error
	if err != nil {
		return nil, err
	}
	err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("status = ?", entity.UserStatusUnregist).Count(&statTotal.TotalUnregist).Error
	if err != nil {
		return nil, err
	}
	return statTotal, nil
}

func (d UserRepo) StatGroupInvite(ctx context.Context, startTime, endTime int64, page, size int) ([]dao.StatGroupUserItem, int, error) {
	//select count(1) as total, parent.user_id as user_id from user  parent inner join user child on parent.user_id = child.up_user_id WHERE parent.status != 3 AND (child.mobile != '' or child.wxa_open_id != '') group by parent.user_id ORDER BY total desc LIMIT 10;
	values := make([]interface{}, 0)
	whereSql := ""
	whereSql = utils.GenQuery(whereSql, "AND", "user.status != ?")
	values = append(values, entity.UserStatusUnregist)
	whereSql = utils.GenQuery(whereSql, "AND", "(child.mobile != ? or child.wxa_open_id != ?)")
	values = append(values, "")
	values = append(values, "")
	if startTime != 0 {
		whereSql = utils.GenQuery(whereSql, "AND", "child.real_regist_time >= ?")
		values = append(values, startTime)
	}
	if endTime != 0 {
		whereSql = utils.GenQuery(whereSql, "AND", "child.real_regist_time <= ?")
		values = append(values, endTime)
	}
	var total int64
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().InnerJoins("inner join user as child on user.user_id = child.up_user_id").Where(whereSql, values...).
		Group("user.user_id").Count(&total).Error
	if err != nil {
		return nil, 0, err
	}
	list := []dao.StatGroupUserItem{}
	err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().InnerJoins("inner join user as child on user.user_id = child.up_user_id").Where(whereSql, values...).
		Select("count(*) as total, user.user_id as user_id").
		Group("user.user_id").Order("total desc").Offset((page - 1) * size).Limit(size).Find(&list).Error
	return list, int(total), err
}

func (d UserRepo) CountByParams(ctx context.Context, whereParams map[string]interface{}) (int, error) {
	whereSql, values, _ := utils.BuildCondition(whereParams)
	total := int64(0)
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where(whereSql, values...).Count(&total).Error
	return int(total), err
}

func (d UserRepo) StatInviteTotal(ctx context.Context) (*dao.StatShareTotalItem, *dao.StatShareTotalItem, error) {
	now := time.Now().UnixNano() / 1e6
	inviteItem, notInviteItem := &dao.StatShareTotalItem{}, &dao.StatShareTotalItem{}
	err := d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("(mobile != ? or wxa_open_id != ?) and up_user_id != ?", "", "", 0).Count(&inviteItem.Total).Error
	if err != nil {
		return nil, nil, err
	}
	err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("(mobile != ? or wxa_open_id != ?) and up_user_id != ? and real_regist_time >= ?", "", "", 0, utils.GetDayTime(now)).Count(&inviteItem.Today).Error
	if err != nil {
		return nil, nil, err
	}
	err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("(mobile != ? or wxa_open_id != ?) and up_user_id != ? and real_regist_time >= ? and real_regist_time < ?", "", "", 0, utils.GetPreNDayTime(now, 1), utils.GetDayTime(now)).Count(&inviteItem.Yesterday).Error
	if err != nil {
		return nil, nil, err
	}
	err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("(mobile != ? or wxa_open_id != ?) and up_user_id = ?", "", "", 0).Count(&notInviteItem.Total).Error
	if err != nil {
		return nil, nil, err
	}
	err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("(mobile != ? or wxa_open_id != ?) and up_user_id = ? and real_regist_time >= ?", "", "", 0, utils.GetDayTime(now)).Count(&notInviteItem.Today).Error
	if err != nil {
		return nil, nil, err
	}
	err = d.db.WithContext(ctx).Model(&dao.User{}).Debug().Where("(mobile != ? or wxa_open_id != ?) and up_user_id = ? and real_regist_time >= ? and real_regist_time < ?", "", "", 0, utils.GetPreNDayTime(now, 1), utils.GetDayTime(now)).Count(&notInviteItem.Yesterday).Error
	if err != nil {
		return nil, nil, err
	}
	return inviteItem, notInviteItem, nil
}
